﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;
using System.IO;

using xEngine.Debugging;
using xEngine.Actors;
using xEngine.Actors.Terrain;

using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

using Color = Microsoft.Xna.Framework.Color;
using Point = System.Drawing.Point;
using Keys = System.Windows.Forms.Keys;

namespace xEditor
{
    public partial class MainWindow : Form
    {
        public static string EditorVersion
        {
            get
            {
                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo("xeditor.exe");
                return fvi.FileMajorPart + "." + fvi.FileMinorPart + "." + fvi.FileBuildPart;
            }
        }

        private bool _loadingObjectInfo;

        public MainWindow()
        {
            InitializeComponent();
            Text = "xEditor v" + EditorVersion;
            LogWriter.OnWriteLine += new EventHandler<LogEventArgs>(OnWriteLine);

            renderingControl1.MouseDown += new MouseEventHandler(delegate { renderingControl1.Select(); });
            console.LostFocus += new EventHandler(delegate { console.Height = 0; });
        }

        #region Generic

        private bool Confirm(string text)
        {
            return MessageBox.Show(text, "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;
        }
        private bool SaveCheck()
        {
            return true;
        }
        private string SplitCapitalised(string s)
        {
            return s;
        }

        #endregion

        #region Events

        private void OnActorSelected(object s, EventArgs e)
        {
            Actor[] selection = renderingControl1.GetSelection();

            // Clear selection
            browserList_Select(null);

            // Add selection
            foreach (Actor actor in selection)
                browserList_Select(actor);

            LoadProps();
        }
        delegate void OnWriteLineCallback(object s, LogEventArgs e);
        private void OnWriteLine(object s, LogEventArgs e)
        {
            if (console.InvokeRequired && !IsDisposed)
                Invoke(new OnWriteLineCallback(OnWriteLine), new object[] { s, e });
            else
            {
                if (!console.IsDisposed)
                {
                    console.AppendText(e.Message + "\n");

                    if (console.Height == 0)
                    {
                        console.Height = 200;
                        console.Select();
                        console.Focus();
                    }
                }
            }
        }
        private void OnCameraChanged(object s, EventArgs e)
        {
            Camera c = (Camera)s;

            status_cameraPos.Text = c.Transformation.Position.ToString();
            status_cameraDir.Text = c.Transformation.Direction.ToString();
        }
        private void OnActorAdded(object s, EventArgs e)
        {
            Actor actor = (Actor)s;

            ListViewItem item = new ListViewItem(actor.Name);
            item.Tag = actor;
            browserList.Items.Add(item);
        }
        private void OnActorUpdated(object s, EventArgs e)
        {
            LoadProps();
        }

        private void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.F:
                    if(!renderingControl1.Focused) break;
                    Actor[] selected = renderingControl1.GetSelection();

                    if (selected.Length > 0)
                        Camera.DefaultCamera.LookAt(selected[0]);
                    break;
            }
        }

        #endregion

        #region Toolbar

        private void tb_modeChange(object sender, EventArgs e)
        {
            tb_mode_select.Checked = tb_mode_move.Checked = tb_mode_rotate.Checked = tb_mode_scale.Checked = false;
            ((ToolStripButton)sender).Checked = true;

            renderingControl1.SceneMode = (SceneMode)Enum.Parse(typeof(SceneMode), ((ToolStripButton)sender).Text);
        }

        #endregion

        #region Object Browser

        private void browserList_Select(Actor actor)
        {
            if (actor == null)
                browserList.SelectedItems.Clear();
            else
            {
                ListViewItem item = browserList_FindItem(actor);

                if (item != null)
                {
                    int index = browserList.Items.IndexOf(item);
                    if (!browserList.SelectedIndices.Contains(index))
                        browserList.SelectedIndices.Add(index);
                }
            }
        }
        private ListViewItem browserList_FindItem(Actor actor)
        {
            foreach (ListViewItem item in browserList.Items)
                if (item.Tag == actor)
                    return item;

            return null;
        }
        private void browserList_SelectedIndexChanged(object sender, EventArgs e)
        {
            renderingControl1.ClearSelection();

            foreach (ListViewItem item in browserList.SelectedItems)
                renderingControl1.AddSelection((Actor)item.Tag);

            LoadProps();
        }
        private void browserList_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete && browserList.SelectedItems.Count > 0 && Confirm("Delete selected objects?"))
            {
                Actor actor;
                foreach (ListViewItem item in browserList.SelectedItems)
                {
                    actor = (Actor)item.Tag;
                    browserList.Items.Remove(item);
                    renderingControl1.RemoveActor(actor);
                }                
            }
        }

        #endregion

        #region Main Menu

        private void menu_fileNewScene(object sender, EventArgs e)
        {
            if (SaveCheck())
            {
                renderingControl1.ClearScene();
                browserList.Clear();
            }
        }
        private void menu_fileQuit(object sender, EventArgs e)
        {
            if (SaveCheck())
            {
                Close();
            }
        }
        private void menu_terrainCreateNew(object sender, EventArgs e)
        {
            Terrain t = renderingControl1.CreateNewTerrain(int.Parse(((ToolStripMenuItem)sender).Text.Split('x')[0]));
            browserList_FindItem(t).Text = (t.Name += " " + ((ToolStripMenuItem)sender).Text);
        }
        private void menu_terrainImport(object sender, EventArgs e)
        {
            if (importHeightMapDialog.ShowDialog() == DialogResult.OK)
            {
                HeightMapType type = HeightMapType.Raw16;
                if (importHeightMapDialog.FilterIndex == 1)
                    type = HeightMapType.Png8;
                else if (importHeightMapDialog.FilterIndex == 2)
                    type = HeightMapType.Raw8;

                Actor[] selected = renderingControl1.GetSelection();

                if (selected.Length == 1 && selected[0].GetType() == typeof(Terrain) && Confirm("Do you want to replace selected terrain?"))
                    ((Terrain)selected[0]).LoadHeightMap(importHeightMapDialog.FileName, type);
                else
                {
                    Terrain t = renderingControl1.LoadHeightMap(importHeightMapDialog.FileName, type);
                    browserList_FindItem(t).Text = t.Name = Path.GetFileNameWithoutExtension(importHeightMapDialog.FileName);
                }
            }
        }
        private void menu_terrainExport(object sender, EventArgs e)
        {
            Actor[] selected = renderingControl1.GetSelection();

            if (selected.Length == 1 && selected[0].GetType() == typeof(Terrain))
            {
                exportHeightMapDialog.FileName = selected[0].Name;
                if(exportHeightMapDialog.ShowDialog() == DialogResult.OK)
                {
                    HeightMapType type = HeightMapType.Raw16;
                    if (exportHeightMapDialog.FilterIndex == 1)
                        type = HeightMapType.Png8;
                    else if (exportHeightMapDialog.FilterIndex == 2)
                        type = HeightMapType.Raw8;

                    ((Terrain)selected[0]).SaveHeightMap(exportHeightMapDialog.FileName, type);
                }
            }
            else
                MessageBox.Show("No terrain selected");
        }
        private void menu_terrainFlatten(object sender, EventArgs e)
        {
            Actor[] selected = renderingControl1.GetSelection();

            if (selected.Length == 1 && selected[0].GetType() == typeof(Terrain) && Confirm("Do you want to flatten selected terrain?"))
                ((Terrain)selected[0]).ResetHeightMap();
        }

        #endregion

        #region Object Properties

        private void LoadProps()
        {
            Actor[] selection = renderingControl1.GetSelection();
          
            _loadingObjectInfo = true;
            if (selection.Length == 0)
            {
                p_type.Text = p_name.Text = "N/A";
                p_name.Enabled = false;

                p_pos_x.Text = p_pos_y.Text = p_pos_z.Text = p_rot_x.Text = p_rot_y.Text = p_rot_z.Text = p_sca_x.Text = p_sca_y.Text = p_sca_z.Text = "N/A";
                p_pos_x.Enabled = p_pos_y.Enabled = p_pos_z.Enabled = p_rot_x.Enabled = p_rot_y.Enabled = p_rot_z.Enabled = p_sca_x.Enabled = p_sca_y.Enabled = p_sca_z.Enabled = false;
            }
            else if (selection.Length == 1)
            {
                p_type.Text = selection[0].GetType().Name;
                p_name.Text = selection[0].Name;

                p_pos_x.Text = selection[0].Transformation.Position.X.ToString();
                p_pos_y.Text = selection[0].Transformation.Position.Y.ToString();
                p_pos_z.Text = selection[0].Transformation.Position.Z.ToString();

                p_rot_x.Text = MathHelper.ToDegrees(selection[0].Transformation.Rotation.X).ToString();
                p_rot_y.Text = MathHelper.ToDegrees(selection[0].Transformation.Rotation.Y).ToString();
                p_rot_z.Text = MathHelper.ToDegrees(selection[0].Transformation.Rotation.Z).ToString();

                p_sca_x.Text = selection[0].Transformation.Scale.X.ToString();
                p_sca_y.Text = selection[0].Transformation.Scale.Y.ToString();
                p_sca_z.Text = selection[0].Transformation.Scale.Z.ToString();

                p_name.Enabled = true;
                p_pos_x.Enabled = p_pos_y.Enabled = p_pos_z.Enabled = p_rot_x.Enabled = p_rot_y.Enabled = p_rot_z.Enabled = p_sca_x.Enabled = p_sca_y.Enabled = p_sca_z.Enabled = true;
            }
            else
            {
                bool same = true;
                string prev = null;
                
                #region General Info

                p_type.Text = "Multiple Actors Selected";
                p_name.Enabled = true;
                p_name.Text = "-";

                #endregion
                #region Transformation - Position
                
                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Position.X.ToString();
                    else if (prev != actor.Transformation.Position.X.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_pos_x.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Position.Y.ToString();
                    else if (prev != actor.Transformation.Position.Y.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_pos_y.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Position.Z.ToString();
                    else if (prev != actor.Transformation.Position.Z.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_pos_z.Text = same ? prev : "-";
                prev = null; same = true;

                p_pos_x.Enabled = p_pos_y.Enabled = p_pos_z.Enabled = true;
                
                #endregion
                #region Transformation - Rotation

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Rotation.X.ToString();
                    else if (prev != actor.Transformation.Rotation.X.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_rot_x.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Rotation.Y.ToString();
                    else if (prev != actor.Transformation.Rotation.Y.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_rot_y.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Rotation.Z.ToString();
                    else if (prev != actor.Transformation.Rotation.Z.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_rot_z.Text = same ? prev : "-";
                prev = null; same = true;

                p_rot_x.Enabled = p_rot_y.Enabled = p_rot_z.Enabled = true;

                #endregion
                #region Transformation - Scale

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Scale.X.ToString();
                    else if (prev != actor.Transformation.Scale.X.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_sca_x.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Scale.Y.ToString();
                    else if (prev != actor.Transformation.Scale.Y.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_sca_y.Text = same ? prev : "-";
                prev = null; same = true;

                foreach (Actor actor in selection)
                {
                    if (prev == null)
                        prev = actor.Transformation.Scale.Z.ToString();
                    else if (prev != actor.Transformation.Scale.Z.ToString())
                    {
                        same = false;
                        break;
                    }
                }
                p_sca_z.Text = same ? prev : "-";
                prev = null; same = true;

                p_sca_x.Enabled = p_sca_y.Enabled = p_sca_z.Enabled = true;

                #endregion
            }
            _loadingObjectInfo = false;
        }

        private void p_name_TextChanged(object sender, EventArgs e)
        {
            if (!_loadingObjectInfo)
            {
                Actor[] selection = renderingControl1.GetSelection();
                foreach(Actor actor in selection)
                    browserList_FindItem(actor).Text = actor.Name = p_name.Text;
                
                PropsUpdated();
            }
        }        
        private void p_rot_TextChanged(object sender, EventArgs e)
        {
            if (!_loadingObjectInfo)
            {
                TextBox t = (TextBox)sender;
                Vector3 v;
                float f = 0;

                if (float.TryParse(t.Text, out f))
                {
                    f = MathHelper.ToRadians(f);

                    Actor[] selection = renderingControl1.GetSelection();
                    foreach (Actor actor in selection)
                    {
                        v = actor.Transformation.Rotation;

                        switch (t.Name.Replace("p_rot_", ""))
                        {
                            case "x": v.X = f; break;
                            case "y": v.Y = f; break;
                            case "z": v.Z = f; break;
                        }

                        actor.Transformation.Rotation = v;
                    }
                    PropsUpdated();
                }
            }
        }
        private void p_pos_TextChanged(object sender, EventArgs e)
        {
            if (!_loadingObjectInfo)
            {
                TextBox t = (TextBox)sender;
                Vector3 v;
                float f = 0;

                if (float.TryParse(t.Text, out f))
                {
                    Actor[] selection = renderingControl1.GetSelection();
                    foreach (Actor actor in selection)
                    {
                        v = actor.Transformation.Position;

                        switch (t.Name.Replace("p_pos_", ""))
                        {
                            case "x": v.X = f; break;
                            case "y": v.Y = f; break;
                            case "z": v.Z = f; break;
                        }

                        actor.Transformation.Position = v;
                    }
                    PropsUpdated();
                }
            }
        }
        private void p_sca_TextChanged(object sender, EventArgs e)
        {
            if (!_loadingObjectInfo)
            {
                TextBox t = (TextBox)sender;
                Vector3 v;
                float f = 0;

                if (float.TryParse(t.Text, out f))
                {
                    Actor[] selection = renderingControl1.GetSelection();
                    foreach (Actor actor in selection)
                    {
                        v = actor.Transformation.Scale;

                        switch (t.Name.Replace("p_sca_", ""))
                        {
                            case "x": v.X = f; break;
                            case "y": v.Y = f; break;
                            case "z": v.Z = f; break;
                        }

                        actor.Transformation.Scale = v;
                    }
                    PropsUpdated();
                }
            }
        }

        private void PropsUpdated()
        {

        }

        #endregion                            
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainWindow());
        }
    }
}
